from enum import Enum
from typing import List, Union, Optional

from bct.tools.common.bct4.base_dto import JsonDTO


class CalcDaysType(Enum):
    """
    计算天数类型
    """
    # 含头不含尾
    INCLUSIVE_EXCLUSIVE = 'INCLUSIVE_EXCLUSIVE'
    # 不含头不含尾
    EXCLUSIVE_EXCLUSIVE = 'EXCLUSIVE_EXCLUSIVE'
    # 含头含尾
    INCLUSIVE_INCLUSIVE = 'INCLUSIVE_INCLUSIVE'
    # 不含头含尾
    EXCLUSIVE_INCLUSIVE = 'EXCLUSIVE_INCLUSIVE'


class ProductType(Enum):
    """
    产品类型
    """
    VANILLA_RANGE_PROTECTIVE = 'VANILLA_RANGE_PROTECTIVE'
    VANILLA_RANGE_PROTECTIVE_VERTICAL_SPREAD = 'VANILLA_RANGE_PROTECTIVE'
    VANILLA_LIMIT_RANGE_PROTECTIVE = 'VANILLA_LIMIT_RANGE_PROTECTIVE'
    VANILLA_LIMIT_RANGE_PROTECTIVE_VERTICAL_SPREAD = 'VANILLA_LIMIT_RANGE_PROTECTIVE_VERTICAL_SPREAD'
    VANILLA_EUROPEAN = 'VANILLA_EUROPEAN'
    VANILLA_AMERICAN = 'VANILLA_AMERICAN'
    VANILLA_PARTICIPATION_CALL = 'VANILLA_PARTICIPATION_CALL'
    VANILLA_DOUBLE_PARTICIPATION_CALL = 'VANILLA_DOUBLE_PARTICIPATION_CALL'
    VANILLA_PARTICIPATION_SPREAD_CALL = 'VANILLA_PARTICIPATION_SPREAD_CALL'
    VERTICAL_SPREAD = 'VERTICAL_SPREAD'
    TRIPLE_VERTICAL_SPREAD_CALL = 'TRIPLE_VERTICAL_SPREAD_CALL'
    TRIPLE_VERTICAL_SPREAD_PUT = 'TRIPLE_VERTICAL_SPREAD_PUT'
    SHARK_FIN = 'SHARK_FIN'
    DOUBLE_SHARK_FIN = 'DOUBLE_SHARK_FIN'
    EAGLE = 'EAGLE'
    DIGITAL_EUROPEAN = 'DIGITAL_EUROPEAN'
    TOUCH = 'TOUCH'
    NO_TOUCH = 'NO_TOUCH'
    DOUBLE_TOUCH = 'DOUBLE_TOUCH'
    DOUBLE_NO_TOUCH = 'DOUBLE_NO_TOUCH'
    CONCAVE = 'CONCAVE'
    CONVEX = 'CONVEX'
    DOUBLE_DIGITAL_CALL = 'DOUBLE_DIGITAL_CALL'
    DOUBLE_DIGITAL_PUT = 'DOUBLE_DIGITAL_PUT'
    TRIPLE_DIGITAL_CALL = 'TRIPLE_DIGITAL_CALL'
    TRIPLE_DIGITAL_PUT = 'TRIPLE_DIGITAL_PUT'
    RANGE_ACCRUALS = 'RANGE_ACCRUALS'
    RANGE_ACCRUALS_DIGITAL_CALL = 'RANGE_ACCRUALS_DIGITAL_CALL'
    RANGE_ACCRUALS_DIGITAL_PUT = 'RANGE_ACCRUALS_DIGITAL_PUT'
    RANGE_ENHANCE = 'RANGE_ENHANCE'
    PROTECTIVE_CALL = 'PROTECTIVE_CALL'
    STRADDLE = 'STRADDLE'
    STRANGLE = 'STRANGLE'
    ASIAN_CALL = 'ASIAN_CALL'
    ASIAN_PUT = 'ASIAN_PUT'
    ASIAN_ENHANCEMENT_CALL = 'ASIAN_ENHANCEMENT_CALL'
    ASIAN_ENHANCEMENT_PUT = 'ASIAN_ENHANCEMENT_PUT'
    MODEL_XY = 'MODEL_XY'
    FORWARD = 'FORWARD'
    KNOCK_IN_DOWN_CALL = 'KNOCK_IN_DOWN_CALL'
    KNOCK_IN_DOWN_PUT = 'KNOCK_IN_DOWN_PUT'
    SNOWBALL_FLOATING = 'SNOWBALL_FLOATING'
    SNOWBALL_FLOATING_CALL = 'SNOWBALL_FLOATING_CALL'
    SNOWBALL_FLOATING_PUT = 'SNOWBALL_FLOATING_PUT'
    SNOWBALL_FLOATING_NONE = 'SNOWBALL_FLOATING_NONE'
    SNOWBALL_FLOATING_PARTIAL = 'SNOWBALL_FLOATING_PARTIAL'
    SNOWBALL_DISCOUNT_OPEN = 'SNOWBALL_DISCOUNT_OPEN'
    SNOWBALL_DISCOUNT_OPEN_PARTIAL_PROTECTIVE = 'SNOWBALL_DISCOUNT_OPEN_PARTIAL_PROTECTIVE'
    SNOWBALL_DOUBLE_DIGITAL = 'SNOWBALL_DOUBLE_DIGITAL'
    SNOWBALL_PARACHUTE = 'SNOWBALL_PARACHUTE'
    SNOWBALL_AUTO_REDEEM_SPREAD_CALL = 'SNOWBALL_AUTO_REDEEM_SPREAD_CALL'
    SNOWBALL_ENHANCE = 'SNOWBALL_ENHANCE'
    SNOWBALL_ENHANCE_PARTIAL_PROTECTIVE = 'SNOWBALL_ENHANCE_PARTIAL_PROTECTIVE'
    SNOWBALL_FLOATING_NONE_STEP_KNOCK_IN = 'SNOWBALL_FLOATING_NONE_STEP_KNOCK_IN'
    BOOSTER_CALL = 'BOOSTER_CALL'
    PHOENIX_FLOATING_PARTIAL = 'PHOENIX_FLOATING_PARTIAL'
    PHOENIX_FLOATING_PARTIAL_DUE_COUPON = 'PHOENIX_FLOATING_PARTIAL_DUE_COUPON'
    PHOENIX_FLOATING_PARTIAL_IMMEDIATE_COUPON = 'PHOENIX_FLOATING_PARTIAL_IMMEDIATE_COUPON'
    PHOENIX_FLOATING_NONE_DUE_COUPON = 'PHOENIX_FLOATING_NONE_DUE_COUPON'
    PHOENIX_FLOATING_NONE_IMMEDIATE_COUPON = 'PHOENIX_FLOATING_NONE_IMMEDIATE_COUPON'
    AIRBAG_VANILLA = 'AIRBAG_VANILLA'
    AIRBAG_SPREAD = 'AIRBAG_SPREAD'
    AIRBAG_PROTECTIVE = 'AIRBAG_PROTECTIVE'
    AIRBAG_SPREAD_PROTECTIVE = 'AIRBAG_SPREAD_PROTECTIVE'
    AIRBAG_UPPER_CAPPING = 'AIRBAG_UPPER_CAPPING'


class FindTradesPagedDTO(JsonDTO):
    """
    分页查询交易请求
    """
    page: int = 0
    page_size: int = 200
    trade_ids: Optional[List[str]] = None
    book_names: Optional[List[str]] = None
    product_types: Optional[List[ProductType]] = None

    def to_dict(self):
        """
        :return:
        """
        ret = {
            "page": self.page,
            "pageSize": self.page_size
        }
        if self.trade_ids:
            ret['tradeIds'] = self.trade_ids
        if self.book_names:
            ret['bookNames'] = self.book_names
        if self.product_types:
            types = []
            for p in self.product_types:
                types.append(p.name)
            ret['productTypes'] = types
        return ret


class FindTrades(JsonDTO):
    trade_ids: List[str]
    trade_record_id: Optional[str]
    product_types: Optional[List[str]] = None
    counterparty_ids: Optional[List[str]] = None

    def __init__(self, trade_ids=[]):
        self.trade_ids = trade_ids
        self.trade_record_id = None

    def to_dict(self):
        ret = {
            'tradeIds': self.trade_ids
        }
        if self.trade_record_id:
            ret['tradeRecordId'] = self.trade_record_id
        if self.product_types:
            ret['productTypes'] = self.product_types
        if self.counterparty_ids:
            ret['counterpartyIds'] = self.counterparty_ids

        return ret


class CallOrPutEnum(Enum):
    CALL = 'CALL'
    PUT = 'PUT'


class ProductTypeEnum(Enum):
    # 非保本雪球
    SNOWBALL_FLOATING_NONE = 'SNOWBALL_FLOATING_NONE'
    VANILLA_EUROPEAN = 'VANILLA_EUROPEAN'


# 敲入支付方式
class KnockOutPaymentTypeEnum(Enum):
    # 立即支付
    immediate_payment = "IMMEDIATE_PAYMENT"
    # 到期支付
    due_payment = "DUE_PAYMENT"


# 计算天数类型
class CalcDaysTypeEnum(Enum):
    # 含头不含尾
    inclusive_exclusive = 'INCLUSIVE_EXCLUSIVE'
    # 不含头不含尾
    exclusive_exclusive = 'EXCLUSIVE_EXCLUSIVE'
    # 含头含尾
    inclusive_inclusive = 'INCLUSIVE_INCLUSIVE'
    # 不含头含尾
    exclusive_inclusive = 'EXCLUSIVE_INCLUSIVE'


# 计算天数截止日 类型
class CalcDaysDeadlineTypeEnum(Enum):
    # 结算日
    SETTLEMENT_DATE = "SETTLEMENT_DATE"
    # 期末观察日
    LAST_OBSERVATION_DATE = "LAST_OBSERVATION_DATE"


# 名义本金额类型
class NotionalTypeEnum(Enum):
    # 交易货币
    TRADE_CURRENCY = 'TRADE_CURRENCY'
    # 结算货币
    SETTLEMENT_CURRENCY = 'SETTLEMENT_CURRENCY'


class DirectionTypeEnum(Enum):
    BUYER = 'BUYER'
    SELLER = 'SELLER'
    NONE = 'NONE'


class PriceTypeEnum(Enum):
    CLOSE = 'CLOSE'
    SETTLE = 'SETTLE'
    HEDGE = 'HEDGE'
    VWAP = 'VWAP'
    TWAP = 'TWAP'


class PremiumTypeEnum(Enum):
    CNY = 'CNY'
    PERCENT = 'PERCENT'


# 计息基准
class AccrualNotionTypeEnum(Enum):
    # 预付金余额
    PRE_PAY_AMOUNT = 'PRE_PAY_AMOUNT'
    # 名义本金额
    NOTIONAL = 'NOTIONAL'


# 我方
class MyPartyRoleEnum(Enum):
    FIRST_PARTY = 'FIRST_PARTY'
    SECOND_PARTY = 'SECOND_PARTY'


class BusinessTypeEnum(Enum):
    SWAP = 'SWAP'
    OPTION = 'OPTION'
    # 场内期权
    LISTED_OPTION = 'LISTED_OPTION'
    # 收益凭证
    INCOME_CERTIFICATE = 'INCOME_CERTIFICATE'


class Product(JsonDTO):
    # yyyy-mm-dd
    initial_date: str
    # 参与率 1 = 100%
    participation: str = '1'
    # 行权价格系数 1 = 100%
    strike_price_ratio: str = '1'
    # 最低收益率 1 = 100%
    minimum_coupon_rate: str = '0'
    # 金额保留位数
    value_precision: int = 2
    # 价格保留位数
    price_precision: int = 4
    # 计算价格天数类型
    calc_days_type: CalcDaysTypeEnum = CalcDaysTypeEnum.inclusive_exclusive
    # 交易日历
    calendar_names: List[str] = ["默认交易日历"]
    # 计算天数截止日 类型
    calc_days_deadline_type: CalcDaysDeadlineTypeEnum = CalcDaysDeadlineTypeEnum.LAST_OBSERVATION_DATE
    # 标的物
    instrument_id: str
    # instrumentName
    instrument_name: str
    # ?
    calc_trade_flag = {"flag": False}
    # 是否年化
    annualized: bool = True
    # 年化天数
    annualized_days: int = 365
    # 名义本金额类型
    notional_type: NotionalTypeEnum = NotionalTypeEnum.SETTLEMENT_CURRENCY
    # 行权价格
    strike_price: str
    # 期初汇率（未使用）
    initial_x_rate: str = '1'
    settlement_currency: str = 'CNY'
    trade_currency: str = 'CNY'
    display_trade_currency: str = 'CNY'
    display_settlement_currency: str = 'CNY'
    settlement_date: str
    # 计算天数，用于年化计算
    counted_days: str = ''
    # 期初价格
    initial_price: str
    # 前端期权费率
    initial_premium_rate: str
    # 前端期权费支付日
    initial_premium_payment_date: str
    # 前端期权费
    initial_premium: str
    # 自定义字段
    customize = {}
    # 名义本金
    notional: str
    # 产品类型
    product_type: Union[ProductTypeEnum, str]

    def __init__(self, param: dict = None):
        # todo 补全构造函数
        self.product_type = param.get('productType', "")
        self.customize = {}
        if 'customize' in param and isinstance(param['customize'], dict):
            for i in param['customize'].keys():
                self.customize[i] = param['customize'][i]

    def to_dict(self):
        return {
            "initialDate": self.initial_date,
            "participation": self.participation,
            "strikePriceRatio": self.strike_price_ratio,
            "minimumCouponRate": self.minimum_coupon_rate,
            "valuePrecision": self.value_precision,
            "pricePrecision": self.price_precision,
            "calcDaysType": self.calc_days_type.value,
            "calendarNames": self.calendar_names,
            "calcDaysDeadlineType": self.calc_days_deadline_type.value,
            "instrumentId": self.instrument_id,
            "instrumentName": self.instrument_name,
            "calcTradeFlag": self.calc_trade_flag,
            "annualized": self.annualized,
            "annualizedDays": self.annualized_days,
            "notionalType": self.notional_type.value,

            "strikePrice": self.strike_price,
            "initialXRate": self.initial_x_rate,
            "settlementCurrency": self.settlement_currency,
            "tradeCurrency": self.trade_currency,
            "displayTradeCurrency": self.display_trade_currency,
            "displaySettlementCurrency": self.display_settlement_currency,
            "settlementDate": self.settlement_date,
            "countedDays": self.counted_days,
            "initialPrice": self.initial_price,
            "initialPremiumRate": self.initial_premium_rate,
            "initialPremiumPaymentDate": self.initial_premium_payment_date,
            "initialPremium": self.initial_premium,
            "customize": self.customize,
            "notional": self.notional,
            "productType": self.product_type.value,
        }


# 产品字段
class TradeDTO(JsonDTO):
    annualized: bool = True
    mark_to_market: str = "NONE"
    net_settlement: bool = False
    trade_date: str = "2021-10-25"
    annualized_days: int = 365
    price_precision: int = 4
    value_precision: int = 2
    direction: DirectionTypeEnum
    calc_days_type: CalcDaysTypeEnum = CalcDaysTypeEnum.inclusive_exclusive
    price_type: PriceTypeEnum = PriceTypeEnum.CLOSE
    premium_type: PremiumTypeEnum = PremiumTypeEnum.PERCENT
    # 计息基准
    accrual_notion_type: AccrualNotionTypeEnum = AccrualNotionTypeEnum.NOTIONAL
    my_party_role: MyPartyRoleEnum = MyPartyRoleEnum.FIRST_PARTY
    business_type: BusinessTypeEnum = BusinessTypeEnum.OPTION
    hedge_party_name: str = ''
    calculation_agent_name: str = ''
    settlement_currency: str = 'CNY'
    notional_type: NotionalTypeEnum = NotionalTypeEnum.SETTLEMENT_CURRENCY
    fx_rate_precision: int = 4
    calc_days_deadline_type: CalcDaysDeadlineTypeEnum = CalcDaysDeadlineTypeEnum.LAST_OBSERVATION_DATE
    # 交易id
    trade_id: str
    # 交易日历
    calendar_names: List[str] = ["默认交易日历"]
    initial_date: str
    last_observation_date: str
    display_trade_currency: str = 'CNY'
    display_settlement_currency: str = 'CNY'
    trade_currency: str = 'CNY'
    notional: str
    initial_x_rate: str = '1.0'
    # 期初期权费率
    initial_premium_rate: str
    # 期初期权费
    initial_premium: str

    # 初始预付金率 非必填，%
    initial_pre_pay_ratio: str = ''
    # 初始预付金
    initial_pre_pay_amount: str = ''
    # 初始预付金支付日
    initial_pre_pay_date: str = ''

    # 追加预付金率 非必填，%
    second_pre_pay_ratio: str = ''
    # 追加预付金
    second_pre_pay_amount: str = ''
    # 追加预付金支付日
    second_pre_pay_date: str = ''

    # 预付金返息率
    pre_pay_premium_rate: str = ''
    # 预付金返息
    pre_pay_premium: str = ''
    # 预付金返息支付日
    pre_pay_premium_payment_date: str = ''

    counted_days: str = ''
    initial_premium_payment_date: str
    # 交易簿
    book_name: str

    trade_confirmation_id: str = ''
    capital_account_name: str
    counterparty_name: str
    margin_calc_mode: str = "不计保证金"

    products: List[Product]

    def __init__(self, param: dict = None):
        self.products = []
        if param:
            if 'products' in param and isinstance(param['products'], List):
                products = param['products']
                for p in products:
                    self.products.append(Product(p))

    def to_dict(self):
        product_list = []
        if self.products:
            for p in self.products:
                product_list.append(p.to_dict())
        return {
            "annualized": self.annualized,
            "markToMarket": "NONE",
            "netSettlement": self.net_settlement,
            "tradeDate": self.trade_date,
            "annualizedDays": self.annualized_days,
            "pricePrecision": self.price_precision,
            "valuePrecision": self.value_precision,
            "direction": self.direction.value,
            "calcDaysType": self.calc_days_type.value,
            "priceType": self.price_type.value,
            "premiumType": self.premium_type.value,
            "accrualNotionType": self.accrual_notion_type.value,
            "myPartyRole": self.my_party_role.value,
            "businessType": self.business_type.value,
            "hedgePartyName": self.hedge_party_name,
            "calculationAgentName": self.calculation_agent_name,
            "settlementCurrency": self.settlement_currency,
            "notionalType": self.notional_type.value,
            "fxRatePrecision": self.fx_rate_precision,
            "calcDaysDeadlineType": self.calc_days_deadline_type.value,
            "tradeId": self.trade_id,
            "calendarNames": self.calendar_names,
            "initialDate": self.initial_date,
            "lastObservationDate": self.last_observation_date,
            "displayTradeCurrency": self.display_trade_currency,
            "displaySettlementCurrency": self.display_settlement_currency,
            "notional": self.notional,
            "initialXRate": self.initial_x_rate,
            "initialPremiumRate": self.initial_premium_rate,
            "initialPremium": self.initial_premium,
            "initialPrePayRatio": self.initial_pre_pay_ratio,
            "initialPrePayAmount": self.initial_pre_pay_amount,
            "initialPrePayDate": self.initial_pre_pay_date,
            "secondPrePayRatio": self.second_pre_pay_ratio,
            "secondPrePayAmount": self.second_pre_pay_amount,
            "secondPrePayDate": self.second_pre_pay_date,
            "prePayPremiumRate": self.pre_pay_premium_rate,
            "prePayPremium": self.pre_pay_premium,
            "prePayPremiumPaymentDate": self.pre_pay_premium_payment_date,

            "tradeCurrency": self.trade_currency,
            "countedDays": self.counted_days,
            "initialPremiumPaymentDate": self.initial_premium_payment_date,
            "bookName": self.book_name,
            "tradeConfirmationId": self.trade_confirmation_id,
            "capitalAccountName": self.capital_account_name,
            "counterpartyName": self.counterparty_name,
            "marginCalcMode": self.margin_calc_mode,
            "products": product_list
        }


# 敲入价格类型
class KnockInPriceType(Enum):
    # 收盘价格
    CLOSE = 'CLOSE'
    # 期货结算价格
    SETTLE = 'SETTLE'
    # 对冲价格
    HEDGE = 'HEDGE'
    # 成交量加权平均价格(VWAP)
    VWAP = 'VWAP'
    # 时间加权平均价格(TWAP)
    TWAP = 'TWAP'


# 敲入属性
class KnockInFeature(JsonDTO):
    # 敲入价格
    knock_in_price: str = ''
    # 敲入价格类型
    knock_in_price_type: KnockInPriceType = KnockInPriceType.CLOSE
    # 敲入价格比率
    knock_in_price_ratio: str = ''
    # 敲入最低基准收益率 部分结构必填：%
    knock_in_min_coupon_rate: str = ''

    def to_dict(self):
        return {
            "knockInMinCouponRate": self.knock_in_min_coupon_rate,
            "knockInPrice": self.knock_in_price,
            "knockInPriceRatio": self.knock_in_price_ratio,
            "knockInPriceType": self.knock_in_price_type.value
        }
